package com.xy.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;

import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.PropertyValuesHolder;
import com.nineoldandroids.animation.ValueAnimator;

/**
 * 2d平滑变化的显示图片的ImageView
 *
 * @author Ybao
 */
public class SmoothImageView extends ImageView {
    private static final int STATE_NORMAL = 0;
    private static final int STATE_TRANSFORM_IN = 1;
    private static final int STATE_TRANSFORM_OUT = 2;
    private int mState = STATE_NORMAL;
    private boolean reSetTransform = false;
    private boolean isTransformStart = false;
    private int mOriginalWidth;
    private int mOriginalHeight;
    private int mOriginalLocationX;
    private int mOriginalLocationY;
    private Bitmap mBitmap;
    private Transfrom mTransfrom;
    private TransformListener mTransformListener;
    private int duration = 300;

    public void setDuration(int duration) {
        this.duration = duration;
    }

    public SmoothImageView(Context context) {
        super(context);
    }

    public SmoothImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SmoothImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setOriginalInfo(int width, int height, int locationX, int locationY) {
        mOriginalWidth = width;
        mOriginalHeight = height;
        mOriginalLocationX = locationX;
        mOriginalLocationY = locationY;
        reSetTransform = true;
    }


    /**
     * 用于开始进入的方法。 调用此方前，需已经调用过setOriginalInfo
     */
    public void transformIn(Bitmap bitmap) {
        reSetTransform = true;
        mState = STATE_TRANSFORM_IN;
        setImageBitmap(bitmap);
    }

    /**
     * 用于开始退出的方法。 调用此方前，需已经调用过setOriginalInfo
     */
    public void transformOut(Bitmap bitmap) {
        reSetTransform = true;
        mState = STATE_TRANSFORM_OUT;
        setImageBitmap(bitmap);
    }

    /**
     * 用于开始进入的方法。 调用此方前，需已经调用过setOriginalInfo
     */
    public void transformIn() {
        mState = STATE_TRANSFORM_IN;
        isTransformStart = true;
        invalidate();
    }

    /**
     * 用于开始退出的方法。 调用此方前，需已经调用过setOriginalInfo
     */
    public void transformOut() {
        mState = STATE_TRANSFORM_OUT;
        isTransformStart = true;
        invalidate();
    }

    public void setBackgroundAlpha(int Alpha) {
        Drawable drawable = getBackground();
        if (drawable != null) {
            drawable.setAlpha(Alpha);
        }
    }


    /**
     * 初始化进入的变量信息
     */
    private Transfrom getTransform() {
        if (getDrawable() == null) {
            return null;
        }
        if (mBitmap == null || mBitmap.isRecycled()) {
            if (getDrawable() instanceof BitmapDrawable){
                mBitmap = ((BitmapDrawable)getDrawable()).getBitmap();
            }
            if (getDrawable() instanceof GlideBitmapDrawable){
                mBitmap = ((GlideBitmapDrawable)getDrawable()).getBitmap();
            }
        }
        if (getWidth() == 0 || getHeight() == 0 || mBitmap.getWidth() == 0 || mBitmap.getHeight() == 0) {
            return null;
        }
        Transfrom mTransfrom = new Transfrom();
        /** 下面为缩放的计算 */
        /* 计算初始的缩放值，初始值因为是CENTR_CROP效果，所以要保证图片的宽和高至少1个能匹配原始的宽和高，另1个大于 */
        float xSScale = mOriginalWidth / ((float) mBitmap.getWidth());
        float ySScale = mOriginalHeight / ((float) mBitmap.getHeight());
        float startScale = xSScale > ySScale ? xSScale : ySScale;
        mTransfrom.startScale = startScale;
        /* 计算结束时候的缩放值，结束值因为要达到FIT_CENTER效果，所以要保证图片的宽和高至少1个能匹配原始的宽和高，另1个小于 */
        float xEScale = getWidth() / ((float) mBitmap.getWidth());
        float yEScale = getHeight() / ((float) mBitmap.getHeight());
        float endScale = xEScale < yEScale ? xEScale : yEScale;
        mTransfrom.endScale = endScale;

        mTransfrom.startBgAlpha = 0;
        mTransfrom.endBgAlpha = 255;
        /**
         * 下面计算Canvas Clip的范围，也就是图片的显示的范围，因为图片是慢慢变大，并且是等比例的，所以这个效果还需要裁减图片显示的区域
         * ，而显示区域的变化范围是在原始CENTER_CROP效果的范围区域
         * ，到最终的FIT_CENTER的范围之间的，区域我用LocationSizeF更好计算
         * ，他就包括左上顶点坐标，和宽高，最后转为Canvas裁减的Rect.
         */
        /* 开始区域 */
        int[] screenLocation = new int[2];
        getLocationOnScreen(screenLocation);
        mTransfrom.startRect = new LocationSizeF();
        mTransfrom.startRect.left = mOriginalLocationX;
        mTransfrom.startRect.top = mOriginalLocationY-screenLocation[1];
        mTransfrom.startRect.width = mOriginalWidth;
        mTransfrom.startRect.height = mOriginalHeight;
        /* 结束区域 */
        mTransfrom.endRect = new LocationSizeF();
        float bitmapEndWidth = mBitmap.getWidth() * mTransfrom.endScale;// 图片最终的宽度
        float bitmapEndHeight = mBitmap.getHeight() * mTransfrom.endScale;// 图片最终的宽度
        mTransfrom.endRect.left = (getWidth() - bitmapEndWidth) / 2;
        mTransfrom.endRect.top = (getHeight() - bitmapEndHeight) / 2;
        mTransfrom.endRect.width = bitmapEndWidth;
        mTransfrom.endRect.height = bitmapEndHeight;
        mTransfrom.rect = new LocationSizeF();
        return mTransfrom;
    }

    public void animClone() {
        mState = STATE_NORMAL;
        setImageDrawable(null);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mState == STATE_NORMAL) {
            setBackgroundAlpha(255);
            super.onDraw(canvas);
        } else {
            if (reSetTransform || mTransfrom == null) {
                reSetTransform = false;
                mTransfrom = getTransform();
                isTransformStart = true;
            }
            if (mTransfrom == null) {
                super.onDraw(canvas);
                return;
            }
            if (isTransformStart) {
                if (mState == STATE_TRANSFORM_IN) {
                    mTransfrom.initStartIn();
                } else {
                    mTransfrom.initStartOut();
                }
            }
            int saveCount = canvas.getSaveCount();
            canvas.save();
            canvas.translate(mTransfrom.rect.left, mTransfrom.rect.top);
            canvas.clipRect(0, 0, mTransfrom.rect.width, mTransfrom.rect.height);
            canvas.scale(mTransfrom.scale, mTransfrom.scale);
            getDrawable().draw(canvas);
            canvas.restoreToCount(saveCount);

            setBackgroundAlpha(mTransfrom.bgAlpha);

            if (isTransformStart) {
                isTransformStart = false;
                if (mState == STATE_TRANSFORM_IN) {
                    startInTransform();
                } else {
                    startOutTransform();
                }
            }
        }
    }

    private void startInTransform() {
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        PropertyValuesHolder scaleHolder = PropertyValuesHolder.ofFloat("scale", mTransfrom.startScale, mTransfrom.endScale);
        PropertyValuesHolder leftHolder = PropertyValuesHolder.ofFloat("left", mTransfrom.startRect.left, mTransfrom.endRect.left);
        PropertyValuesHolder topHolder = PropertyValuesHolder.ofFloat("top", mTransfrom.startRect.top, mTransfrom.endRect.top);
        PropertyValuesHolder widthHolder = PropertyValuesHolder.ofFloat("width", mTransfrom.startRect.width, mTransfrom.endRect.width);
        PropertyValuesHolder heightHolder = PropertyValuesHolder.ofFloat("height", mTransfrom.startRect.height, mTransfrom.endRect.height);
        PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofInt("alpha", mTransfrom.startBgAlpha, mTransfrom.endBgAlpha);
        valueAnimator.setValues(scaleHolder, leftHolder, topHolder, widthHolder, heightHolder, alphaHolder);
        valueAnimator.addUpdateListener(new SmoothAnimatorListener());
        valueAnimator.addListener(new EndAnimatorListener(STATE_TRANSFORM_IN));
        valueAnimator.start();
    }


    private void startOutTransform() {
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        PropertyValuesHolder scaleHolder = PropertyValuesHolder.ofFloat("scale", mTransfrom.endScale, mTransfrom.startScale);
        PropertyValuesHolder leftHolder = PropertyValuesHolder.ofFloat("left", mTransfrom.endRect.left, mTransfrom.startRect.left);
        PropertyValuesHolder topHolder = PropertyValuesHolder.ofFloat("top", mTransfrom.endRect.top, mTransfrom.startRect.top);
        PropertyValuesHolder widthHolder = PropertyValuesHolder.ofFloat("width", mTransfrom.endRect.width, mTransfrom.startRect.width);
        PropertyValuesHolder heightHolder = PropertyValuesHolder.ofFloat("height", mTransfrom.endRect.height, mTransfrom.startRect.height);
        PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofInt("alpha", mTransfrom.endBgAlpha, mTransfrom.startBgAlpha);
        valueAnimator.setValues(scaleHolder, leftHolder, topHolder, widthHolder, heightHolder, alphaHolder);
        valueAnimator.addUpdateListener(new SmoothAnimatorListener());
        valueAnimator.addListener(new EndAnimatorListener(STATE_TRANSFORM_OUT));
        valueAnimator.start();
    }


    public void setOnTransformListener(TransformListener listener) {
        mTransformListener = listener;
    }

    public class SmoothAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
        @Override
        public synchronized void onAnimationUpdate(ValueAnimator animation) {
            mTransfrom.scale = (Float) animation.getAnimatedValue("scale");
            mTransfrom.rect.left = (Float) animation.getAnimatedValue("left");
            mTransfrom.rect.top = (Float) animation.getAnimatedValue("top");
            mTransfrom.rect.width = (Float) animation.getAnimatedValue("width");
            mTransfrom.rect.height = (Float) animation.getAnimatedValue("height");
            mTransfrom.bgAlpha = (Integer) animation.getAnimatedValue("alpha");
            invalidate();
        }
    }

    public class EndAnimatorListener implements ValueAnimator.AnimatorListener {
        private int state;

        public EndAnimatorListener(int state) {
            this.state = state;
        }

        @Override
        public void onAnimationStart(Animator animation) {
        }

        @Override
        public void onAnimationRepeat(Animator animation) {
        }

        @Override
        public void onAnimationEnd(Animator animation) {
                /*
                 * 如果是进入的话，当然是希望最后停留在center_crop的区域。但是如果是out的话，就不应该是center_crop的位置了
                 * ， 而应该是最后变化的位置，因为当out的时候结束时，不回复视图是Normal，要不然会有一个突然闪动回去的bug
                 */

            if (state == STATE_TRANSFORM_IN) {
                mState = STATE_NORMAL;
            }
            if (mTransformListener != null) {
                mTransformListener.onTransformComplete(state);
            }
        }

        @Override
        public void onAnimationCancel(Animator animation) {
        }
    }


    private class Transfrom {
        float startScale;// 图片开始的缩放值
        float endScale;// 图片结束的缩放值
        float scale;// 属性ValueAnimator计算出来的值
        LocationSizeF startRect;// 开始的区域
        LocationSizeF endRect;// 结束的区域
        LocationSizeF rect;// 属性ValueAnimator计算出来的值
        int startBgAlpha;
        int endBgAlpha;
        int bgAlpha;

        void initStartIn() {
            scale = startScale;
            bgAlpha = startBgAlpha;
            try {
                rect = (LocationSizeF) startRect.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }

        void initStartOut() {
            scale = endScale;
            bgAlpha = endBgAlpha;
            try {
                rect = (LocationSizeF) endRect.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }

    }

    private class LocationSizeF implements Cloneable {
        float left;
        float top;
        float width;
        float height;

        @Override
        public String toString() {
            return "[left:" + left + " top:" + top + " width:" + width + " height:" + height + "]";
        }

        @Override
        public Object clone() throws CloneNotSupportedException {
            // TODO Auto-generated method stub
            return super.clone();
        }
    }

    public interface TransformListener {
        /**
         * @param mode STATE_TRANSFORM_IN 1 ,STATE_TRANSFORM_OUT 2
         */
        void onTransformComplete(int mode);// mode 1
    }
}